package com.googlecode.imanager.core.base;

import com.googlecode.imanager.client.Container;
import com.googlecode.imanager.client.Control;
import com.googlecode.imanager.client.Document;
import com.googlecode.imanager.client.Label;
import com.googlecode.imanager.client.Manager;
import com.googlecode.imanager.client.Panel;
import com.googlecode.imanager.client.Picture;
import com.googlecode.imanager.client.bridge.IControl;
import com.googlecode.imanager.client.bridge.IDocument;
import com.googlecode.imanager.client.bridge.IFocusable;
import com.googlecode.imanager.client.bridge.ILabel;
import com.googlecode.imanager.client.bridge.IPicture;
import com.googlecode.imanager.client.bridge.form.IButton;
import com.googlecode.imanager.client.bridge.form.IInputCheck;
import com.googlecode.imanager.client.bridge.form.IInputList;
import com.googlecode.imanager.client.bridge.form.IInputText;
import com.googlecode.imanager.client.bridge.form.ITextArea;
import com.googlecode.imanager.client.bridge.layout.IScrollBox;
import com.googlecode.imanager.client.bridge.layout.IScroller;
import com.googlecode.imanager.client.bridge.style.IBorder;
import com.googlecode.imanager.client.bridge.style.IChildThemeHandler;
import com.googlecode.imanager.client.bridge.style.IParentThemeHandler;
import com.googlecode.imanager.client.bridge.style.IStyle;
import com.googlecode.imanager.client.form.Button;
import com.googlecode.imanager.client.form.CheckBox;
import com.googlecode.imanager.client.form.ComboBox;
import com.googlecode.imanager.client.form.FocusControl;
import com.googlecode.imanager.client.form.InputCheck;
import com.googlecode.imanager.client.form.InputControl;
import com.googlecode.imanager.client.form.InputList;
import com.googlecode.imanager.client.form.InputText;
import com.googlecode.imanager.client.form.ListBox;
import com.googlecode.imanager.client.form.RadioButton;
import com.googlecode.imanager.client.form.TextArea;
import com.googlecode.imanager.client.form.TextBox;
import com.googlecode.imanager.client.layout.ScrollBox;
import com.googlecode.imanager.client.layout.Scroller;
import com.googlecode.imanager.client.style.Theme;
import com.googlecode.imanager.client.style.listener.StyleAttribute;
import com.googlecode.imanager.client.style.listener.StyleWrapper;
import com.googlecode.imanager.client.util.ControlState;
import com.googlecode.imanager.core.base.style.ComputedAttribute;
import com.googlecode.imanager.core.base.style.ComputedStyle;
import com.googlecode.imanager.core.base.style.ComputedTheme;

/**
 * @author wellsantos@wellsantos.com
 * @since 0.01
 */
public abstract class AbstractControl extends BridgeControl implements IControl, IChildThemeHandler, IParentThemeHandler {

    protected final ComputedStyle computedStyle;
    protected final Control source;
    
    public AbstractControl(BridgeApplication bridge, Control source) {
        super(bridge);
        this.source = source;
        this.computedStyle = new ComputedStyle();
        bridge.getManager().setImpl(source, this);
    }
    
    public abstract Object getTarget();
    
    public Control getSource() {
        return source;
    }
    
    @Override
    protected void destroy() {
        if (source instanceof Document){
            getComputedTheme().destroy();
        }
        super.destroy();
    }

    public abstract ComputedTheme getComputedTheme();
    
    public final ComputedTheme getParentComputedTheme(){
        Document doc = source.getDocument();
        if (doc != null){
            return ((AbstractControl) Manager.getImpl(doc)).getComputedTheme();
        }
        else {
            throw new IllegalStateException("no such parent document");
        }
    }
    
    @Override
    public <T> void onStyleAttributeChanged(StyleWrapper styleWrapper, StyleAttribute<T> attribute, T attributeValue) {
        
        if (styleWrapper.getControl() != source){
            if (source instanceof Document){
                getComputedTheme().onStyleAttributeChanged(styleWrapper, attribute, attributeValue);
            }
            doComputeStyle(StyleAttribute.ALL);
        } else {
            doComputeStyle(attribute);
        }
    }
    
    @Override
    public void onStateChanged(ControlState state) {
        doComputeStyle(StyleAttribute.ALL);
    }
    
    @SuppressWarnings("unchecked")
    protected <T> void doComputeStyle(StyleAttribute<T> attribute) {

        getComputedTheme().computeStyle(computedStyle, source.getClass().getName(), source.getState());
        ComputedAttribute<T> dst = (ComputedAttribute<T>) attribute.getAttributeValue(computedStyle);
        
        dst.mergeWith(attribute.getAttributeValue(source.getStyle()));
        
        if (attribute.getAttributeType() == IStyle.class
                || attribute.getAttributeType() == IBorder.class){
            doChangeInsets();
        }
    }

    protected void doChangeInsets() {
        if (!(source instanceof Container)){
            return;
        }
        getManager().setInsets((Container)source, 
                computedStyle.getBorderLeft().getInset(), 
                computedStyle.getBorderTop().getInset(), 
                computedStyle.getBorderRight().getInset(),
                computedStyle.getBorderBottom().getInset());
    }

    public IStyle getComputedStyle() {
        return computedStyle;
    }
    
    @Override
    public boolean isFireStyleChangesToChildren() {
        return true;
    }

    @Override
    public void onThemeAttach(Theme theme) {
        getComputedTheme().onThemeAttach(theme);
    }

    @Override
    public void onThemeDetach(Theme theme) {
        getComputedTheme().onThemeDetach(theme);
    }

    @Override
    public void processException(Exception e){
        Document document = source.getDocument();
        if (document != null){
            document.handleError(e);
        } else {
            getBridgeApplication().handleError(e);
        }
    }
    
    protected void doInitialAssignment(){
        doInitialControlAssignment();
        
        if (source instanceof Container){
            doInitialContainerAssignment();

            if (source instanceof Panel){
                doInitialPanelAssignment();
            }
            else if (source instanceof ScrollBox){
                doInitialScrollBoxAssignment();                

                if (source instanceof Document){
                    doInitialDocumentAssignment();
                }            
            }
            
        }        
        else if (source instanceof Label){
            doInitialLabelAssignment();
        }
        else if (source instanceof Picture){
            doInitialPictureAssignment();
        }
        else if (source instanceof FocusControl){
            doInitialFocusControlAssignment();
            
            if (source instanceof InputControl){
                doInitialInputControlAssignment();

                if (source instanceof InputText){
                    doInitialInputTextAssignment();

                    if (source instanceof TextBox){
                        doInitialTextBoxAssignment();
                    }
                    else if (source instanceof TextArea){
                        doInitialTextAreaAssignment();
                    }
                }
                else if (source instanceof InputList<?>){
                    doInitialInputListAssignment();                    

                    if (source instanceof ComboBox<?>){
                        doInitialComboBoxAssignment();
                    }
                    else if (source instanceof ListBox<?>){
                        doInitialListBoxAssignment();
                    }
                }
                else if (source instanceof InputCheck){
                    doInitialInputCheckAssignment();
                    
                    if (source instanceof CheckBox){
                        doInitialCheckButtonAssignment();
                    }
                    else if (source instanceof RadioButton){
                        doInitialRadioButtonAssignment();
                    }
                }
            }
            else if (source instanceof Button){
                doInitialButtonAssignment();
            }
        }
        
        // TODO inicializar demais tipos de controls
    
        doComputeStyle(StyleAttribute.ALL);
    }
    
    protected void doInitialControlAssignment(){
        IControl c = this;
        Control s = source;
        
        c.setParent(s.getParent());
        c.setAction(s.getAction());
        c.setHint(s.getHint());
        c.setStyle(s.getStyle());
        c.setVisible(s.isVisible());
        c.setDisabled(s.isDisabled());
        c.setBounds(s.getLeft(), s.getTop(), s.getWidth(), s.getHeight());        
    }
    
    protected void doInitialContainerAssignment(){
        // nothing to do
    }
    
    protected void doInitialPanelAssignment(){
        // nothing to do
    }
    
    private void doInitialScrollBoxAssignment() {
        IScrollBox c = (IScrollBox) this;
        ScrollBox s = (ScrollBox) source;

        doInitialScrollerAssignment((IScroller) c.getHorizontalScroller(), s.getHorizontalScroller());
        doInitialScrollerAssignment((IScroller) c.getVerticalScroller(), s.getVerticalScroller());
    }

    private void doInitialScrollerAssignment(IScroller c, Scroller s) {
        c.setOverflow(s.getOverflow());
        c.setLength(s.getLength());
        c.setOffset(s.getOffset());
    }
    
    protected void doInitialDocumentAssignment(){
        IDocument c = (IDocument) this;
        Document s = (Document) source;
        
        c.setModal(s.isModal());
        c.setTitle(s.getTitle());
        c.setPageSettings(s.getPageSettings());

        this.getComputedTheme().onThemeAttach(s.getTheme());
    }
    
    protected void doInitialLabelAssignment() {
        ILabel c = (ILabel) this;
        Label s = (Label) source;
        
        c.setText(s.getText());
        c.setOverflowHorz(s.isOverflowHorz());
        c.setOverflowVert(s.isOverflowVert());
    }

    protected void doInitialPictureAssignment() {
        IPicture c = (IPicture) this;
        Picture s = (Picture) source;
        
        c.setImage(s.getImage());
    }

    private void doInitialFocusControlAssignment() {
        IFocusable c = (IFocusable) this;
        FocusControl s = (FocusControl) source;
        
        c.setAccessKey(s.getAccessKey());
        c.setTabIndex(s.getTabIndex());
    }

    private void doInitialInputControlAssignment() {
        // nothing to do
    }

    private void doInitialInputTextAssignment() {
        IInputText c = (IInputText) this;
        InputText s = (InputText) source;
        
        c.setMaxLength(s.getMaxLength());
        c.setText(s.getText());
    }

    private void doInitialTextBoxAssignment() {
        // nothing to do
    }

    private void doInitialTextAreaAssignment() {
        ITextArea c = (ITextArea) this;
        TextArea s = (TextArea) source;
        
        c.setWantTabKeys(s.isWantTabKeys());
        c.setWrapMode(s.getWrapMode());
    }

    private void doInitialButtonAssignment() {
        IButton c = (IButton) this;
        Button s = (Button) source;
        
        c.setText(s.getText());
    }

    @SuppressWarnings("unchecked")
    private <T> void doInitialInputListAssignment() {
        IInputList<T> c = (IInputList<T>) this;
        InputList<T> s = (InputList<T>) source;
        
        c.setItems(s.getItems());
        c.setFormatter(s.getFormatter());
        c.setSelectedIndex(s.getSelectedIndex());
    }

    private void doInitialComboBoxAssignment() {
        // nothing todo        
    }

    private void doInitialListBoxAssignment() {
        // nothing todo
    }

    private void doInitialInputCheckAssignment() {
        IInputCheck c = (IInputCheck) this;
        InputCheck s = (InputCheck) source;
        
        c.setChecked(s.getChecked());
        c.setGroup(s.getGroup());
        c.setText(s.getText());
    }

    private void doInitialCheckButtonAssignment() {
        // nothing todo
    }

    private void doInitialRadioButtonAssignment() {
        // nothing todo
    }

}
